/*
 * GuildFraktionInfo - list the reputation and the guild level of all members of a wow guild
 * Copyright (C) [2012 - 2013] [www.marantis.de]
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 
 * License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later 
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if not, 
 * see <http://www.gnu.org/licenses/>.
 */
package de.marantis.app.wow.gfi.control;

import de.marantis.app.wow.gfi.beans.ConfigBean;
import de.marantis.app.wow.gfi.beans.GuildCharacterBean;
import de.marantis.app.wow.gfi.beans.ReputationBean;
import de.marantis.app.wow.gfi.helper.ArmoryGuildHelper;
import de.marantis.app.wow.gfi.helper.ConfigHelper;
import de.marantis.app.wow.gfi.helper.HttpHelper;
import java.net.URL;
import java.util.ArrayList;
import org.apache.log4j.Logger;

public abstract class MainController {

    private final static Logger log = Logger.getLogger(MainController.class);
    protected ConfigBean config;
    protected final static int maxMemberPerPage = 100;
    protected static boolean memberPageLoaded;
    public ArrayList<GuildCharacterBean> chars2 ;
    /*
     * *** CONSTRUCTOR *************************************************************************************************
     */
    protected MainController() throws IllegalArgumentException {
        this.config = new ConfigBean();
    }

    protected MainController(ConfigBean config) throws IllegalArgumentException {
        this.config = config;
    }

    /*
     * *** ABSTRACT FUNCTION *******************************************************************************************
     */
    /**
     * Will be called when an armory url is wrong!
     *
     * @param link
     */
    protected abstract void setErrorUrl(String link);

    /**
     * Will be called when a new status message does exist
     *
     * @param msg
     */
    protected abstract void setStatus(String msg);

    /**
     * Event: check for the scan start
     */
    protected abstract void scanStartCheck();

    /**
     * Event: start the scan
     */
    protected abstract void scanStart();

    /**
     * Event: the scan is finished
     */
    protected abstract void scanEnd(ArrayList<GuildCharacterBean> chars);

    
    /**
     * Event: the guild member list is loaded
     */
    protected abstract void fireGuildMemberPageLoad(int idx);
    
    /**
     * Event: the guild member list is loaded
     */
    protected abstract void memberListLoaded(ArrayList<GuildCharacterBean> chars);

    /*
     * Event: start the next repuations thread
     */
    protected abstract void startReputationThread(GuildCharacterBean guildChar, int completeCount, int actualIdx, HtmlLoadTypeEnum type);

    /*
     * Event: start the next item level thread
     */
    protected abstract void startItemLevelThread(GuildCharacterBean guildChar, int completeCount, int actualIdx, HtmlLoadTypeEnum type);
    
    /*
     * *** CONTROLLER MAIN FUNCTION ************************************************************************************
     */
    /**
     * Start the scan
     */
    public void scanArmory() {
        this.scanArmoryThread();
    }

    /**
     * Start the scan in the actual thread
     */
    protected void scanArmoryThread() {
        
        ArrayList<GuildCharacterBean> chars = null;
        
        try {
            //*** call the abstract event function ***
            this.setStatus("");

            //*** call the abstract event function ***
            this.scanStartCheck();

            if (this.config.getArmoryUrl() == null) {
                this.setStatus("Undefined armory url!");
                return;
            }

            //*** call the abstract event function ***
            this.scanStart();

            //*** get the guild member list ***
            chars = this.getGuildMember();
            //System.out.println(chars.toString());
            

            //*** call the abstract event function ***
            this.memberListLoaded(chars);
            

            //*** get other threads a little bit time ***
            Thread.yield();

            //*** get the fraktion infos for any member ***
            this.getReputation(chars);

            //*** finish/repeat msgbox ***
            String msg = "\nScan complete";
            this.setStatus(msg);

        } catch (Exception e) {
            log.error(e);

        } finally {
            //*** call the abstract event function ***
            this.scanEnd(chars);
        }
    }

    /*
     * *** CONTROLLER THREAD FUNCTION **********************************************************************************
     */
    protected void getReputation(ArrayList<GuildCharacterBean> chars) {
        try {
            if (chars == null || chars.size() <= 0) {
                return;
            }

            //*** load all reputations ***
            int idx = 0;
            for (GuildCharacterBean charBean : chars) {
                this.startReputationThread(charBean, chars.size(), idx, HtmlLoadTypeEnum.START_FIRST_TRY);
                this.startItemLevelThread(charBean, chars.size(), idx, HtmlLoadTypeEnum.START_FIRST_TRY);
                idx += 1;
                
                if (this.config.isTestMode()){
                    if (idx >= 10) {
                        break;
                    }
                }
            }

            //*** reload all error reputations ***
            for (GuildCharacterBean charBean : chars) {
                if (charBean.isErrorFlag()) {
                    this.startReputationThread(charBean, chars.size(), idx, HtmlLoadTypeEnum.START_ERROR_RETRY);
                    this.startItemLevelThread(charBean, chars.size(), idx, HtmlLoadTypeEnum.START_ERROR_RETRY);
                }
                idx += 1;
                
                if (this.config.isTestMode()){
                    if (idx >= 10) {
                        break;
                    }
                }
            }

        } catch (Exception e) {
            log.error(e);
        }
    }


    /*
     * *** COMMON CONTROLLER FUNCTION **********************************************************************************
     */
    /* 
     * analyze the member pages. scan all member pages (not just the first one)
     */
    protected ArrayList<GuildCharacterBean> getGuildMember() {
        ArrayList<GuildCharacterBean> chars = new ArrayList<GuildCharacterBean>();
        try {
            int idx = 1;
            int errorRetries = 0;
            int errorRetriesMax = 10;
            
            while (true) {
                String page = "?page=" + idx;
                page = this.config.getArmoryUrl().toString() + page;
                URL armoryURL = new URL(page);
                if (armoryURL == null) {
                    return chars;
                }

                this.setStatus("Lade " + idx + ". Mitgliederseite ...");
                this.fireGuildMemberPageLoad(idx);

                /*
                 * Download the armory html page
                 */
                String html = HttpHelper.getHttpContent(armoryURL);
                if (html == null) {
                    this.setStatus("Guild member html page is null!");
                    log.warn("Guild member html page is null!");

                    errorRetries += 1;
                    if (errorRetries >= errorRetriesMax){
                        throw new Exception("Not possible to get the list with the guild members!");
                    }else{
                        Thread.sleep(1000);
                        continue;
                    }
                }

                /*
                 * Analyze the armory html page
                 */
                int countTR = ArmoryGuildHelper.getPageGuildMember(html, chars);
                if (countTR < maxMemberPerPage) {

                    /*
                     * A member page with html-content but without any members?!
                     * It's a false content!
                     */
                    if (countTR <= 0){
                        this.setStatus("Guild member html page is not null but without guild members!");
                        log.warn("Guild member html page is not null but without guild members!");
                        log.warn(html);
                        
                        errorRetries += 1;
                        if (errorRetries >= errorRetriesMax){
                            throw new Exception("Not possible to get the list with the guild members!");
                        }else{
                            Thread.sleep(1000);
                            continue;
                        }
                    }
                    
                    break;
                    
                } else {
                    idx += 1;
                }
            }

        } catch (Exception e) {
            log.error(e);
        } finally {
            this.setStatus("");
            return chars;
        }
    }

    protected Object[][] convertCharArray(ArrayList<GuildCharacterBean> chars) {

        Object[][] data = new Object[chars.size()][5];

        try {
            int idx = 0;
            for (GuildCharacterBean aChar : chars) {
                Object[] row = new Object[5];
                row[0] = aChar;
                row[1] = aChar.getCharLevel();
                row[2] = aChar.getGuildRank();
                row[3] = new ReputationBean();
                data[idx] = row;
                idx += 1;
            }

        } catch (Exception e) {
            log.error(e);

        } finally {
            return data;
        }
    }

    protected void setupArmoryUrl(ConfigBean config, String link) {
        boolean errFlag = ConfigHelper.setupArmoryUrl(config, link);
        if (errFlag) {
            this.setErrorUrl(link);
        }
    }
    

    /*
     * *** GETTER / SETTER *********************************************************************************************
     */
    public synchronized void setMemberPageLoaded(boolean flag) {
        memberPageLoaded = flag;
    }

    public synchronized boolean istMemberPageLoaded() {
        return memberPageLoaded;
    }

	public ArrayList<GuildCharacterBean> getChars2() {
		return chars2;
	}

	public void setChars2(ArrayList<GuildCharacterBean> chars2) {
		this.chars2 = chars2;
	}
    
    
}
